﻿using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using System.Text;
using System.Text.RegularExpressions;
using WhutDoomCheck.Server.Entities;
using WhutDoomCheck.Server.Extentions;
using WhutDoomCheck.Shared.HttpModels;

namespace WhutDoomCheck.Server.Services
{
    public class MetaDataService
    {
        public MetaDataService(ApplicationDbContext dbContext, IDistributedCache cache, ILogger<MetaDataService> logger)
        {
            this.dbContext = dbContext;
            this.cache = cache;
            this.logger = logger;
        }

        private readonly ApplicationDbContext dbContext;
        private readonly IDistributedCache cache;
        private readonly ILogger logger;

        public DoomMeta GetDooms()
        {
            var data = cache.Get<DoomMeta>("DoomsMeta");
            if (data is null)
            {
                data = new DoomMeta(dbContext.Dooms.AsNoTracking().AsEnumerable().GroupBy(x => x.Region).Select(x =>
                new Shared.HttpModels.Region(x.Key, x.GroupBy(x => x.Area).Select(x =>
                   new Area(x.Key, x.GroupBy(x => x.Building).Select(x =>
                     new Building(x.Key, x.GroupBy(x => x.Floor).Select(x =>
                       new Floor(x.Key.ToString(), x.Select(x => new Room(x.RoomNumber, x.BedCount)))))))))));
                cache.Set("DoomsMeta", data);
            }
            return data;
        }

        public async Task SeedDooms(Stream csvStream)
        {
            if (dbContext.Dooms.Any()) throw new Exception("数据表不为空，无法导入数据");
            // 复制流到本地，避免网络流意外关闭
            using var stream = new MemoryStream();
            csvStream.CopyTo(stream);
            stream.Position = 0;

            using var reader = new StreamReader(stream, Encoding.UTF8);
            var csvHead = reader.ReadLine()!;
            var columnCount = csvHead.Split('\u002C').Length;
            var currentLine = 2; // 初始行号

            var dooms = new HashSet<Doom>();

            while (!reader.EndOfStream)
            {
                var line = reader.ReadLine()!;
                var columns = line.Split('\u002C');
                if (columns.Length != columnCount)
                {
                    logger.LogWarning("csv format error: {line}", currentLine);
                }
                try
                {
                    var doom = new Doom()
                    {
                        Region = columns[0],
                        Area = columns[1],
                        Building = columns[2],
                        Floor = int.Parse(columns[3]),
                        RoomNumber = columns[4],
                        BedCount = int.Parse(columns[5]),
                    };
                    dooms.Add(doom);
                }
                catch (Exception ex)
                {
                    logger.LogError("<{line}>:{ex}", line, ex.Message);
                    continue;
                }
                finally { currentLine++; }
            }

            await dbContext.Dooms.AddRangeAsync(dooms);
            await dbContext.SaveChangesAsync();
        }

        public async Task AppendDooms(Stream csvStream)
        {
            using var stream = new MemoryStream();
            csvStream.CopyTo(stream);
            stream.Position = 0;

            using var reader = new StreamReader(stream, Encoding.UTF8);
            var csvHead = reader.ReadLine()!;
            var columnCount = csvHead.Split('\u002C').Length;
            var currentLine = 2; // 初始行号

            var dooms = new HashSet<Doom>();

            while (!reader.EndOfStream)
            {
                var line = reader.ReadLine()!;
                var columns = line.Split('\u002C');
                if (columns.Length != columnCount)
                {
                    logger.LogWarning("csv format error: {line}", currentLine);
                }
                try
                {
                    var doom = new Doom()
                    {
                        Region = columns[0],
                        Area = columns[1],
                        Building = columns[2],
                        Floor = int.Parse(columns[3]),
                        RoomNumber = columns[4],
                        BedCount = int.Parse(columns[5]),
                    };
                    dooms.Add(doom);
                }
                catch (Exception ex)
                {
                    logger.LogError("<{line}>:{ex}", line, ex.Message);
                    continue;
                }
                finally { currentLine++; }
            }

            foreach (var doom in dooms)
            {
                var query = from exsistDoom in dbContext.Dooms 
                            where doom.Area == exsistDoom.Area
                            where doom.Building == exsistDoom.Building
                            where doom.Floor == exsistDoom.Floor
                            where doom.RoomNumber == exsistDoom.RoomNumber
                            select exsistDoom.Id;

                if (!query.Any()) dbContext.Dooms.Add(doom);
            }

            await dbContext.SaveChangesAsync();
        }

        public async Task SeedStudents(Stream csvStream)
        {
            if (dbContext.Counsellors.Any() || dbContext.Students.Any()) throw new Exception("数据表不为空，无法导入数据");
            // 复制流到本地，避免网络流意外关闭
            using var stream = new MemoryStream();
            csvStream.CopyTo(stream);
            stream.Position = 0;

            using var reader = new StreamReader(stream, Encoding.UTF8);
            var csvHead = reader.ReadLine()!;
            var columnCount = csvHead.Split('\u002C').Length;
            var currentLine = 2; // 初始行号

            var counsellors = new HashSet<Counsellor>();
            var students = new Dictionary<string, Student>(20000);

            while (!reader.EndOfStream)
            {
                var line = reader.ReadLine()!;
                var columns = line.Split('\u002C');
                if (columns.Length < columnCount)
                {
                    logger.LogWarning("csv format error: {line}", currentLine);
                }
                try
                {
                    // 处理导员信息
                    if (string.IsNullOrEmpty(columns[19]))
                    {
                        logger.LogError("缺少辅导员信息: <{student}>", $"{columns[2]},{columns[3]},{columns[6]}");

                        currentLine++;
                        continue;
                    }
                    var equalsCounsellor = new Counsellor
                    {
                        Name = columns[19],
                        Id = columns[20]
                    };
                    counsellors.Add(equalsCounsellor);
                    _ = counsellors.TryGetValue(equalsCounsellor, out var counsellor);

                    var isInSchool = columns[10].Trim() != "否";
                    int? doomId = null; 

                    if (isInSchool)
                    {
                        // 只比对楼名和房间号，最大程度容忍数据错误
                        var doomQuery = from doom in dbContext.Dooms
                                        where doom.Building == columns[13]
                                        where doom.RoomNumber == columns[15]
                                        select doom.Id;
                        if (!doomQuery.Any())
                        {
                            logger.LogWarning("未找到匹配的宿舍信息：{area},{building},{room}", columns[12], columns[13], columns[15]);
                        }
                        else doomId = doomQuery.Single();
                    }

                    var success = int.TryParse(columns[17], out var bedNum);
                    // 处理学生信息
                    var student = new Student
                    {
                        Grade = int.Parse(columns[1]),
                        Sno = columns[2].ToUpper(),
                        Name = columns[3],
                        Gender = columns[4],
                        Ethnic = columns[5],
                        School = columns[6],
                        CultureMethod = columns[7],
                        Major = columns[8],
                        Contact = Regex.Match(columns[9],@"^(\d{11}$)").Groups[1].Value,
                        IsInSchool = isInSchool,
                        CounsellorId = counsellor!.Id,
                        BedNum = success ? bedNum : null,
                        DoomId = doomId,
                         GuideName = columns[21],
                         GuideContact = columns[22],
                    };

                    if (students.ContainsKey(student.Sno))
                    {
                        var exsistingStudent = students[student.Sno];
                        logger.LogError("学号{sno}重复: <{student1}> <{student2}>", student.Sno,
                            $"{exsistingStudent.Name},{exsistingStudent.School}", $"{student.Name},{student.School}");

                        students.Remove(student.Sno);

                        currentLine++;
                        continue;
                    }
                    students.Add(student.Sno, student);
                    currentLine++;
                }
                catch (Exception ex)
                {
                    logger.LogError("<{line}>:{ex}", line, ex.Message);
                    continue;
                }
            }

            logger.LogInformation("parsed {num} lines from csv, valid record {dataNum}.", currentLine - 2, students.Count);

            await dbContext.Counsellors.AddRangeAsync(counsellors);
            await dbContext.Students.AddRangeAsync(students.Values);

            await dbContext.SaveChangesAsync();
        }

        public async Task SeedRegions(Stream csvStream)
        {
            if (dbContext.Regions.Any()) throw new Exception("数据表不为空，无法导入数据");
            using var stream = new MemoryStream();
            csvStream.CopyTo(stream);
            stream.Position = 0;

            using var reader = new StreamReader(stream, Encoding.UTF8);
            var csvHead = reader.ReadLine()!;
            var columnCount = csvHead.Split('\u002C').Length;
            var currentLine = 2; // 初始行号

            var counsellors = new HashSet<Counsellor>();
            var students = new Dictionary<string, Student>(20000);

            while (!reader.EndOfStream)
            {
                var line = reader.ReadLine()!;
                var columns = line.Split('\u002C');
                if (columns.Length < columnCount)
                {
                    logger.LogWarning("csv format error: {line}", currentLine);
                }
                try
                {
                    var region = new Entities.Region()
                    {
                        RegionId = long.Parse(columns[0]),
                        ParentId = string.IsNullOrEmpty(columns[1]) ? 0 : long.Parse(columns[1]),
                        Name = columns[2],
                        Code = columns[3],
                        Level= int.Parse(columns[4]),
                    };
                    dbContext.Regions.Add(region);
                }
                catch (Exception ex)
                {
                    logger.LogError("<{line}>:{ex}", line, ex.Message);
                    continue;
                }
                finally
                {
                    currentLine++;
                }
            }
            await dbContext.SaveChangesAsync();
        }
    }
}
